package com.hqyj.javaSpringCloudAccount.service.impl;

import com.github.pagehelper.PageHelper;

import com.github.pagehelper.PageInfo;
import com.hqyj.entity.account.User;
import com.hqyj.entity.account.UserRole;
import com.hqyj.entity.common.Result;
import com.hqyj.entity.common.SearchBean;
import com.hqyj.entity.test.City;
import com.hqyj.javaSpringCloudAccount.dao.UserDao;
import com.hqyj.javaSpringCloudAccount.dao.UserRoleDao;
import com.hqyj.javaSpringCloudAccount.entity.UserVo;
import com.hqyj.javaSpringCloudAccount.service.TestFeignClient;
import com.hqyj.javaSpringCloudAccount.service.UserService;
import com.hqyj.javaSpringCloudAccount.utils.MD5Util;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.vavr.control.Try;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private UserRoleDao userRoleDao;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private TestFeignClient testFeignClient;

    @Override
    public User getUserById(int id) {
        return userDao.getUserById(id);
    }

    @Override
    public Result<User> login(User user) {
        User userTemp = userDao.getUserByUserNameAndUserPassword(user.getUserName(),
                MD5Util.getMD5(user.getPassword()));
        if (userTemp == null) {
            return new Result<User>(Result.ResultCode.FAILED.code, "Wrong UserName or Password");
        }

        // TODO

        // 返回 result
        return new Result<User>(Result.ResultCode.SUCCESS.code, "SUCCESS", userTemp);
    }

    @Override
    public void logout() {
        // TODO
    }

    @Override
    @Transactional
    public Result<User> insertUser(User user) {
        // 1.查数据库用户或邮箱是否已经被注册
        List<User> users = Optional
                .ofNullable(userDao.getUserByUserNameOrEmail(user.getUserName(),user.getEmail()))
                .orElse(Collections.emptyList());
        if (users.size() > 0) {
            return new Result<User>(Result.ResultCode.FAILED.code, "UserName or Email is Existed");
        }

        // 2.新用户添加
        user.setCreateDate(LocalDateTime.now());
        user.setPassword(MD5Util.getMD5(user.getPassword()));
        userDao.insertUser(user);

        // 3.如果有添加角色，则在UserRole表中添加用户角色
        //     如果选了多个角色，则使用 .forEach 将这几个角色的 id 遍历出来，封装到 UserRole 的 roleId 中
        if (user.getRoles() != null) {
            user.getRoles().stream()
                    .forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(),item.getId()));});
        }
        return new Result<User>(Result.ResultCode.SUCCESS.code, "Insert Success.", user);
    }

    @Override
    public PageInfo<User> getUsersBySearchBean(SearchBean searchBean) {
        //初始化searchBean，如果前端里没有传当前页给默认值
        searchBean.initSearchBean();

        //开始进行分页
        PageHelper.startPage(searchBean.getCurrentPage(),searchBean.getPageSize());
        return new PageInfo<User>(Optional
                .ofNullable(userDao.getUsersBySearchVo(searchBean))
                .orElse(Collections.emptyList()));
    }

    @Override
    @Transactional
    public Result<User> updateUser(User user) {
        // 查询用户名和邮箱是否存在或是否有改动
        List<User> users = Optional
                .ofNullable(userDao.getUserByUserNameOrEmail(user.getUserName(), user.getEmail()))
                .orElse(Collections.emptyList());

        // 如果此用户名或邮箱已存在且不是本人 id，则提示已被占用
        // ** 标记：item -> item.getId() != user.getId() 有疑惑 **
        if (users.stream().filter(item -> item.getId() != user.getId()).count() > 0) {
            return new Result<User>(Result.ResultCode.FAILED.code, "User Name or email is repeat.");
        }

        // 修改内容无被占用情况，开始更新信息
        userDao.updateUser(user);

        // 角色信息的修改
        if (user.getRoles() != null && !user.getRoles().isEmpty()) {
            userRoleDao.deleteUserRoleByUserId(user.getId());
            user.getRoles().stream()
                    .forEach(item -> {userRoleDao.insertUserRole(new UserRole(user.getId(),item.getId()));});
        }
        return new Result<User>(Result.ResultCode.SUCCESS.code, "Update Success.", user);
    }

    @Override
    public Result<Object> deleteUserById(int id) {
        userDao.deleteUserById(id);
        userRoleDao.deleteUserRoleByUserId(id);
        return new Result<Object>(Result.ResultCode.SUCCESS.code, "Delete Success.");
    }

    @Override
    public User getUserByUserName(String userName) {
        return userDao.getUserByUserName(userName);
    }

    @Override
    public List<User> getUsersByKeyWord(String keyWord) {
        SearchBean searchBean = new SearchBean();
        searchBean.setKeyword(keyWord);
        searchBean.initSearchBean();
        return Optional.ofNullable(userDao.getUsersBySearchVo(searchBean)).orElse(Collections.emptyList());
    }

    @Override
    public UserVo getUserVoById(int id) {
        UserVo userVo = new UserVo();
        User user = userDao.getUserById(id);

        // BeanUtils.copyProperties() --- 将 user对象copy到 userVo
        BeanUtils.copyProperties(user, userVo);

       /* // id 命名要和接口定义的一致
        City city = restTemplate.getForObject("http://CLIENT-TEST/api/test/city/{cityId}",
                City.class, 1890);
        userVo.setCity(city);
        return userVo;*/


        /*
         * - 熔断器策略配置
         * - 生产者抛出异常或停掉的情况，都会返回默认值
         */
        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
                .failureRateThreshold(50)
                .waitDurationInOpenState(Duration.ofMillis(1000))
                .build();
        CircuitBreaker circuitBreaker = CircuitBreaker.of("circuitBreakerPloy", circuitBreakerConfig);
        /*Try<City> circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
                circuitBreaker,
                () -> restTemplate.getForObject("http://CLIENT-TEST/api/test/city/{cityId}", City.class, 1890)
        )).recover(Exception.class, new City());*/
        Try<City> circuitBreakerSupplier = Try.ofSupplier(CircuitBreaker.decorateSupplier(
                circuitBreaker,
                () -> testFeignClient.getCityByCityId(1890)
        )).recover(Exception.class, new City());
        userVo.setCity(circuitBreakerSupplier.get());

        return userVo;
    }
}
